/*-----------------------------------------------------------------------*/
/* Low level disk I/O module SKELETON for FatFs     (C)ChaN, 2019        */
/*-----------------------------------------------------------------------*/
/* If a working storage control module is available, it should be        */
/* attached to the FatFs via a glue function rather than modifying it.   */
/* This is an example of glue functions to attach various exsisting      */
/* storage control modules to the FatFs module with a defined API.       */
/*-----------------------------------------------------------------------*/

// clang-format off
#include "os_config.h"
#include "ff.h"     /* Obtains integer types */
#include "diskio.h" /* Declarations of disk functions */
#include <stdbool.h>
// #include "bsp.h"
// clang-format on

#if !defined(FATFS_EN_SD)
#define FATFS_EN_SD 1
#endif

#if !defined(FATFS_EN_MSC)
#define FATFS_EN_MSC 1
#endif


#if defined(GD32F407)
#include "gd32f4xx.h"
#if FATFS_EN_SD
#include "sdcard/gd32sd/drv_sdcard.h"
#endif
#if FATFS_EN_MSC
#include "usb/gd32usb/USB_Host/usb_host_msc_udisk/inc/usbh_usr.h"
#include "usb/gd32usb/host/class/msc/Include/usbh_msc_core.h"
#endif
#elif defined(GD32F10X_HD)
#include "gd32f10x.h"
#elif defined(STM32F10X_HD)
#include "stm32f10x.h"
#elif defined(STM32F40_41xxx)
#include "stm32f4xx.h"
#else
#error "No device selected"
#endif


/* Definitions of physical drive number for each drive */
#define DEV_RAM 2 /* Example: Map Ramdisk to physical drive 0 */
#define DEV_SD 1  /* Example: Map MMC/SD card to physical drive 1 */
#define DEV_USB 0 /* Example: Map USB MSD to physical drive 2 */


static volatile DSTATUS stat[3] = {STA_NOINIT, STA_NOINIT, STA_NOINIT}; /* disk status */
/*-----------------------------------------------------------------------*/
/* Get Drive Status                                                      */
/*-----------------------------------------------------------------------*/

#if defined(GD32F407)

DSTATUS disk_status(
    BYTE pdrv /* Physical drive nmuber to identify the drive */
) {
    int result;
    uint32_t state = 0;
#if FATFS_EN_MSC
    usb_core_driver *udev = (usb_core_driver *)usb_host_msc.data;
#endif
    switch (pdrv) {
    case DEV_RAM:
        stat[pdrv] |= STA_NOINIT;
        break;
    case DEV_SD:
#if FATFS_EN_SD
        result = sd_cardstatus_get(&state);
        if (result == SD_OK)
            stat[pdrv] &= ~STA_NOINIT;
        else
            stat[pdrv] |= STA_NOINIT;
#endif
        break;
    case DEV_USB:
#if FATFS_EN_MSC
        if (udev->host.connect_status) {
            stat[pdrv] &= ~STA_NOINIT;
        } else {
            stat[pdrv] |= STA_NOINIT;
        }
#endif
        break;
    }
    return stat[pdrv];
}



/*-----------------------------------------------------------------------*/
/* Inidialize a Drive                                                    */
/*-----------------------------------------------------------------------*/

DSTATUS disk_initialize(
    BYTE pdrv /* Physical drive nmuber to identify the drive */
) {
    int result;
#if FATFS_EN_MSC
    usb_core_driver *udev = (usb_core_driver *)usb_host_msc.data;
#endif
    switch (pdrv) {
    case DEV_RAM:
        // result = RAM_disk_initialize();
        //  translate the reslut code here

        break;
    case DEV_SD:
#if FATFS_EN_SD
        result = sd_io_init();
        if (result == SD_OK)
            stat[pdrv] &= ~STA_NOINIT;
        else
            stat[pdrv] |= STA_NOINIT;
        // translate the reslut code here
#endif
        break;
    case DEV_USB:
#if FATFS_EN_MSC
        if (udev->host.connect_status) {
            stat[pdrv] &= ~STA_NOINIT;
        }
#endif
        break;
    }
    return stat[pdrv];
}



/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */
/*-----------------------------------------------------------------------*/

DRESULT disk_read(
    BYTE pdrv,    /* Physical drive nmuber to identify the drive */
    BYTE *buff,   /* Data buffer to store read data */
    LBA_t sector, /* Start sector in LBA */
    UINT count    /* Number of sectors to read */
) {
    int result = true;
#if FATFS_EN_MSC
    BYTE status = USBH_OK;
    usb_core_driver *udev = (usb_core_driver *)usb_host_msc.data;
#endif

    if (!count)
        return RES_PARERR;
    if (stat[pdrv] & STA_NOINIT)
        return RES_NOTRDY;

    switch (pdrv) {
    case DEV_RAM:
        // translate the arguments here
        // result = RAM_disk_read(buff, sector, count);
        // translate the reslut code here
        return RES_ERROR;

    case DEV_SD:
#if FATFS_EN_SD
        // translate the arguments here
        if (count > 1)
            result = sd_multiblocks_read((void *)buff, sector * sd_cardinfo.card_blocksize, sd_cardinfo.card_blocksize, count);
        else
            result = sd_block_read((void *)buff, sector * sd_cardinfo.card_blocksize, sd_cardinfo.card_blocksize);
        // result = bzbl_sd_read_multi_blocks(buff,sector*512,512,count);
        // result = SD_ReadDisk(buff, sector, count);
        if (result == SD_OK)
            return RES_OK;
        else
            return RES_ERROR;
#endif
        return RES_ERROR;
        // translate the reslut code here
    case DEV_USB:
#if FATFS_EN_MSC
        if (udev->host.connect_status) {
            do {
                status = usbh_msc_read(&usb_host_msc, pdrv, sector, buff, count);
                if (!udev->host.connect_status) {
                    return RES_ERROR;
                }
            } while (status == USBH_BUSY);
        } else {
            return RES_ERROR;
        }
        if (status == USBH_OK) {
            return RES_OK;
        }
#endif
        return RES_ERROR;
    }
    return RES_PARERR;
}



/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */
/*-----------------------------------------------------------------------*/

#if FF_FS_READONLY == 0

DRESULT disk_write(
    BYTE pdrv,        /* Physical drive nmuber to identify the drive */
    const BYTE *buff, /* Data to be written */
    LBA_t sector,     /* Start sector in LBA */
    UINT count        /* Number of sectors to write */
) {
#if FATFS_EN_MSC
    BYTE status = USBH_OK;
    usb_core_driver *udev = (usb_core_driver *)usb_host_msc.data;
#endif

    int result = true;
    if (!count)
        return RES_PARERR;
    if (stat[pdrv] & STA_NOINIT)
        return RES_NOTRDY;

    switch (pdrv) {
    case DEV_RAM:
        // translate the arguments here
        // result = RAM_disk_write(buff, sector, count);
        // translate the reslut code here
        return RES_ERROR;
    case DEV_SD:
#if FATFS_EN_SD
        // translate the arguments here
        // result = bzbl_sd_write_multi_blocks((uint8_t*)buff, sector*512,512,count);
        if (count > 1)
            result = sd_multiblocks_write((void *)buff, sector * sd_cardinfo.card_blocksize, sd_cardinfo.card_blocksize, count);
        else
            result = sd_block_write((void *)buff, sector * sd_cardinfo.card_blocksize, sd_cardinfo.card_blocksize);
        // result = SD_WriteDisk((uint8_t*) buff, sector, count);
        if (result == SD_OK)
            return RES_OK;
        else
            return RES_ERROR;
        // translate the reslut code here
#endif
        return RES_ERROR;
    case DEV_USB:
#if FATFS_EN_MSC
        if (stat[pdrv] & STA_PROTECT)
            return RES_WRPRT;
        if (udev->host.connect_status) {
            do {
                status = usbh_msc_write(&usb_host_msc, pdrv, sector, (BYTE *)buff, count);
                if (!udev->host.connect_status) {
                    return RES_ERROR;
                }
            } while (status == USBH_BUSY);
        }
        if (status == USBH_OK) {
            return RES_OK;
        }
#endif
        return RES_ERROR;
    }
    return RES_PARERR;
}

#endif


/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/

DRESULT disk_ioctl(
    BYTE pdrv, /* Physical drive nmuber (0..) */
    BYTE cmd,  /* Control code */
    void *buff /* Buffer to send/receive control data */
) {
    int result;
#if FATFS_EN_MSC
    msc_lun info;
#endif

    if (stat[pdrv] & STA_NOINIT)
        return RES_NOTRDY;
    switch (pdrv) {
    case DEV_RAM:

        // Process of the command for the RAM drive
        return RES_ERROR;
    case DEV_SD:
#if FATFS_EN_SD
        switch (cmd) {
        case CTRL_SYNC:
            result = sd_transfer_state_get();
            if (result == SD_NO_TRANSFER)
                return RES_OK;
            else
                return RES_NOTRDY;
            // SD_WaitWriteOperation();
            // while(SD_GetStatus() != SD_TRANSFER_OK);
        case GET_SECTOR_SIZE:
            *(DWORD *)buff = sd_cardinfo.card_blocksize;
            return RES_OK;
        case GET_BLOCK_SIZE:
            *(DWORD *)buff = 1;
            return RES_OK;
        case GET_SECTOR_COUNT:
            *(DWORD *)buff = sd_cardinfo.card_capacity / sd_cardinfo.card_blocksize;
            return RES_OK;
        default:
            return RES_PARERR;
        }
        // Process of the command for the MMC/SD card
#endif
        return RES_PARERR;
    case DEV_USB:
#if FATFS_EN_MSC
        switch (cmd) {
        /* make sure that no pending write process */
        case CTRL_SYNC:
            return RES_OK;
        /* get number of sectors on the disk (dword) */
        case GET_SECTOR_COUNT:
            if (USBH_OK == usbh_msc_lun_info_get(&usb_host_msc, pdrv, &info)) {
                *(DWORD *)buff = (DWORD)info.capacity.block_nbr;
                return RES_OK;
            }
            return RES_ERROR;
        /* get r/w sector size (word) */
        case GET_SECTOR_SIZE:
            if (USBH_OK == usbh_msc_lun_info_get(&usb_host_msc, pdrv, &info)) {
                *(WORD *)buff = (DWORD)info.capacity.block_size;
                return RES_OK;
            }
            return RES_ERROR;
        /* get erase block size in unit of sector (dword) */
        case GET_BLOCK_SIZE:
            *(DWORD *)buff = 512;
            return RES_OK;
        default:
            return RES_PARERR;
        }
#endif
        return RES_PARERR;
    }
    return RES_PARERR;
}

#elif defined(GD32F10X_HD)
#include "gd32f10x.h"
#elif defined(STM32F10X_HD)
#include "stm32f10x.h"
#elif defined(STM32F40_41xxx)
#include "stm32f4xx.h"
#else
#error "No device selected"
#endif

DWORD get_fattime(void) {
    // 年 1980+year 例 2017=1980+37
    // 月 month 1..12
    // 日 day 1..31
    // 时 hour 0..23
    // 分 min  0..59
    // 秒 sec/2 例 50s=25*2 范围0..29
    uint32_t year = 0, month = 1, day = 1, hour = 0, min = 0, sec = 0;
    return year << 25 | month << 21 | day << 16 | hour << 11 | min << 5 | sec;
}
